package cc

//import (
//	"context"
//	"fmt"
//	"github.com/golang-module/carbon"
//	"reflect"
//	"regexp"
//	"strings"
//	"unicode"
//)
//
//// Package check
//// Tips:
//// 1. 关联字段名不能使用 proto 定义时的字段名，需要使用生成 struct 的字段名, 如 date_lte_with=end_date 为错误写法，正确写法：date_lte_with=EndDate
//// 2. 关联字段不能使用别名定义多个规则，如date_lte_with=EndDate需要使用到时间校验规则和对比其他字段规则，底层会用 date_lte_with=EndDate 作为 aliasMap 的 key 取校验器导致取不到
//// 自定义验证规则
//package check
//
//import (
//"context"
//"fmt"
//"xxx/carbon"
//"xxx/util/helpers"
//validate "gopkg.in/go-playground/validator.v9"
//"reflect"
//"regexp"
//"strings"
//"unicode"
//)
//
//const (
//	RequireLte30    = "requireLte30"      // 必填&长度必须小于等于30的校验，eg:身份证宽松限制，
//	Require36       = "require36"         // 必填&长度必须等于36的校验 uuid 数据库主键,
//	RequireLte50    = "requireLte50"      // 长度必须等于50的校验,eg:名称
//	RequireLte64    = "requireLte64"      // 必填&长度必须等于64的校验，eg:orgCode
//	RequireLte100   = "requireLte100"     // 必填&长度必须等于100的校验
//	Options01       = "options0_1"        // int类 bool 判断
//	Mobile          = "mobile"            // 手机号码（1开头的是一位数字）
//	UserMobile      = "userMobile"        // 用户登录手机号码（6-20的字符串）
//	Email           = "email"             // 邮箱
//	Password        = "password"          // 密码
//	VerifyCode4     = "verify_code4"      // 4位验证码
//	SliceRequire36  = "slice_require36"   // 不为空的UUID切片
//	DateTime        = "datetime"          // 日期时间校验 2006-01-02 15:04:05
//	Date            = "date"              // 日期时间校验 2006-01-02
//	Time            = "time"              // 日期时间校验 15:04:05
//	Time_H_S        = "time_H_S"          // 日期时间校验 15:04
//	DateLteWith     = "date_lte_with"     // 日期小于等于另一字段日期
//	DateLtWith      = "date_lt_with"      // 日期小于另一字段日期
//	DateGteWith     = "date_gte_with"     // 日期大于等于另一字段日期
//	DateGtWith      = "date_gt_with"      // 日期大于另一字段日期
//	DatetimeLteWith = "datetime_lte_with" // 日期时间小于等于另一字段日期时间
//	DatetimeLtWith  = "datetime_lt_with"  // 日期时间小于另一字段日期时间
//	DatetimeGteWith = "datetime_gte_with" // 日期时间大于等于另一字段日期时间
//	DatetimeGtWith  = "datetime_gt_with"  // 日期时间大于等于另一字段日期时间
//)
//
//// Go参数校验Validator 定义别名，对应多个校验器
//var aliasMap = map[string]string{
//	Require36:      "required,len=36",
//	VerifyCode4:    "len=4",
//	RequireLte30:   "required,lte=30",
//	RequireLte64:   "required,lte=64",
//	RequireLte50:   "required,lte=50",
//	RequireLte100:  "required,lte=100",
//	Options01:      "oneof=0 1",
//	Mobile:         "len=11,numeric,startNumOne",
//	UserMobile:     "max=20,min=6",
//	Email:          "customEmail",
//	Password:       "gte=6,lte=12",
//	SliceRequire36: "gt=0,dive,required,len=36",
//	DateTime:       "normalDateTime",
//	Date:           "normalDate",
//	Time:           "normalTime",
//	Time_H_S:       "normalTime_H_S",
//}
//
//var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
//var datetimeRegex = regexp.MustCompile("^\\d{4}-(0\\d|1[0-2])-([0-2]\\d|3[0-1]) ([0-1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)$")
//var dateRegex = regexp.MustCompile("^\\d{4}-(0\\d|1[0-2])-([0-2]\\d|3[0-1])$")
//var timeRegex = regexp.MustCompile("^([0-1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)$")
//var time_H_S_Regex = regexp.MustCompile("^([0-1]\\d|2[0-3]):([0-5]\\d)$")
//
//func customEmail() (string, validate.FuncCtx) {
//	return "customEmail", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return emailRegex.MatchString(fl.Field().String())
//	}
//}
//
//func normalDatetime() (string, validate.FuncCtx) {
//	return "normalDateTime", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return datetimeRegex.MatchString(fl.Field().String())
//	}
//}
//
//// 其实这里使用tim标准包time更 // Package check
//// Tips:
//// 1. 关联字段名不能使用 proto 定义时的字段名，需要使用生成 struct 的字段名, 如 date_lte_with=end_date 为错误写法，正确写法：date_lte_with=EndDate
//// 2. 关联字段不能使用别名定义多个规则，如date_lte_with=EndDate需要使用到时间校验规则和对比其他字段规则，底层会用 date_lte_with=EndDate 作为 aliasMap 的 key 取校验器导致取不到
//// 自定义验证规则
//package check
//
//import (
//"context"
//"fmt"
//"xxx/carbon"
//"xxx/util/helpers"
//validate "gopkg.in/go-playground/validator.v9"
//"reflect"
//"regexp"
//"strings"
//"unicode"
//)
//
//const (
//	RequireLte30    = "requireLte30"      // 必填&长度必须小于等于30的校验，eg:身份证宽松限制，
//	Require36       = "require36"         // 必填&长度必须等于36的校验 uuid 数据库主键,
//	RequireLte50    = "requireLte50"      // 长度必须等于50的校验,eg:名称
//	RequireLte64    = "requireLte64"      // 必填&长度必须等于64的校验，eg:orgCode
//	RequireLte100   = "requireLte100"     // 必填&长度必须等于100的校验
//	Options01       = "options0_1"        // int类 bool 判断
//	Mobile          = "mobile"            // 手机号码（1开头的是一位数字）
//	UserMobile      = "userMobile"        // 用户登录手机号码（6-20的字符串）
//	Email           = "email"             // 邮箱
//	Password        = "password"          // 密码
//	VerifyCode4     = "verify_code4"      // 4位验证码
//	SliceRequire36  = "slice_require36"   // 不为空的UUID切片
//	DateTime        = "datetime"          // 日期时间校验 2006-01-02 15:04:05
//	Date            = "date"              // 日期时间校验 2006-01-02
//	Time            = "time"              // 日期时间校验 15:04:05
//	Time_H_S        = "time_H_S"          // 日期时间校验 15:04
//	DateLteWith     = "date_lte_with"     // 日期小于等于另一字段日期
//	DateLtWith      = "date_lt_with"      // 日期小于另一字段日期
//	DateGteWith     = "date_gte_with"     // 日期大于等于另一字段日期
//	DateGtWith      = "date_gt_with"      // 日期大于另一字段日期
//	DatetimeLteWith = "datetime_lte_with" // 日期时间小于等于另一字段日期时间
//	DatetimeLtWith  = "datetime_lt_with"  // 日期时间小于另一字段日期时间
//	DatetimeGteWith = "datetime_gte_with" // 日期时间大于等于另一字段日期时间
//	DatetimeGtWith  = "datetime_gt_with"  // 日期时间大于等于另一字段日期时间
//)
//
//// 定义别名，对应多个校验器
//var aliasMap = map[string]string{
//	Require36:      "required,len=36",
//	VerifyCode4:    "len=4",
//	RequireLte30:   "required,lte=30",
//	RequireLte64:   "required,lte=64",
//	RequireLte50:   "required,lte=50",
//	RequireLte100:  "required,lte=100",
//	Options01:      "oneof=0 1",
//	Mobile:         "len=11,numeric,startNumOne",
//	UserMobile:     "max=20,min=6",
//	Email:          "customEmail",
//	Password:       "gte=6,lte=12",
//	SliceRequire36: "gt=0,dive,required,len=36",
//	DateTime:       "normalDateTime",
//	Date:           "normalDate",
//	Time:           "normalTime",
//	Time_H_S:       "normalTime_H_S",
//}
//
//var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
//var datetimeRegex = regexp.MustCompile("^\\d{4}-(0\\d|1[0-2])-([0-2]\\d|3[0-1]) ([0-1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)$")
//var dateRegex = regexp.MustCompile("^\\d{4}-(0\\d|1[0-2])-([0-2]\\d|3[0-1])$")
//var timeRegex = regexp.MustCompile("^([0-1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)$")
//var time_H_S_Regex = regexp.MustCompile("^([0-1]\\d|2[0-3]):([0-5]\\d)$")
//
//func customEmail() (string, validate.FuncCtx) {
//	return "customEmail", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return emailRegex.MatchString(fl.Field().String())
//	}
//}
//
//func normalDatetime() (string, validate.FuncCtx) {
//	return "normalDateTime", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return datetimeRegex.MatchString(fl.Field().String())
//	}
//}
//
//// 其实这里使用标准包time更准确
//func normalDate() (string, validate.FuncCtx) {
//	return "normalDate", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return dateRegex.MatchString(fl.Field().String())
//	}
//}
//
//func normalTime() (string, validate.FuncCtx) {
//	return "normalTime", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return timeRegex.MatchString(fl.Field().String())
//	}
//}
//
//func normalTime_H_S() (string, validate.FuncCtx) {
//	return "normalTime_H_S", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return time_H_S_Regex.MatchString(fl.Field().String())
//	}
//}
//
//func startNumOne() (string, validate.FuncCtx) {
//	return "startNumOne", func(ctx context.Context, fl validate.FieldLevel) bool {
//		str := string([]rune(fl.Field().String())[:1])
//		return str == "1"
//	}
//}
//
//// trimString 用来将类型为字符串的值去掉头尾的空格
//// 能够修改的数据
//// 1. 单独校验字符串指针类型数据 如: a := " 123 "; pa := &a; globalValidator.VarCtx(context.Background(), pa)
//// 2. 结构体中的字符串指针类型数据
//// 3. 结构体指针对应的结构体中的字符串数据
//// 4. 结构体指针对应的结构体中的字符串指针数据
//func trimString() (string, validate.FuncCtx) {
//	return "trimSpace", func(ctx context.Context, fl validate.FieldLevel) bool {
//		if fl.Field().Kind() == reflect.String && fl.Field().CanSet() {
//			fl.Field().SetString(strings.TrimSpace(fl.Field().String()))
//			return true
//		}
//		return false
//	}
//}
//
//// leftTrimString 用来将类型为字符串的值去掉头部的空格
//// 能够修改的数据
//// 1. 单独校验字符串指针类型数据 如: a := " 123 "; pa := &a; globalValidator.VarCtx(context.Background(), pa)
//// 2. 结构体中的字符串指针类型数据
//// 3. 结构体指针对应的结构体中的字符串数据
//// 4. 结构体指针对应的结构体中的字符串指针数据
//func leftTrimString() (string, validate.FuncCtx) {
//	return "leftTrimSpace", func(ctx context.Context, fl validate.FieldLevel) bool {
//		if fl.Field().Kind() == reflect.String && fl.Field().CanSet() {
//			fl.Field().SetString(strings.TrimLeftFunc(fl.Field().String(), unicode.IsSpace))
//			return true
//		}
//		return false
//	}
//}
//
//// rightTrimString 用来将类型为字符串的值去掉尾部的空格
//// 能够修改的数据
//// 1. 单独校验字符串指针类型数据 如: a := " 123 "; pa := &a; globalValidator.VarCtx(context.Background(), pa)
//// 2. 结构体中的字符串指针类型数据
//// 3. 结构体指针对应的结构体中的字符串数据
//// 4. 结构体指针对应的结构体中的字符串指针数据
//func rightTrimString() (string, validate.FuncCtx) {
//	return "rightTrimSpace", func(ctx context.Context, fl validate.FieldLevel) bool {
//		if fl.Field().Kind() == reflect.String && fl.Field().CanSet() {
//			fl.Field().SetString(strings.TrimRightFunc(fl.Field().String(), unicode.IsSpace))
//			return true
//		}
//		return false
//	}
//}
//
//// 日期与另一字段日期对比大小，小于等于则返回true,如果两个字段值其一为空，则不比较，返回true
//func dateLteWith() (string, validate.FuncCtx) {
//	return "date_lte_with", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return compareDateOrDateTime(fl, "date", "lte")
//	}
//}
//
//// 日期与另一字段日期对比大小，小于则返回true,如果两个字段值其一为空，则不比较，返回true
//func dateLtWith() (string, validate.FuncCtx) {
//	return "date_lt_with", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return compareDateOrDateTime(fl, "date", "lt")
//	}
//}
//
//// 日期与另一字段日期对比大小，大于等于则返回true,如果两个字段值其一为空，则不比较，返回true
//func dateGteWith() (string, validate.FuncCtx) {
//	return "date_gte_with", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return compareDateOrDateTime(fl, "date", "gte")
//	}
//}
//
//// 日期与另一字段日期对比大小，大于则返回true,如果两个字段值其一为空，则不比较，返回true
//func dateGtWith() (string, validate.FuncCtx) {
//	return "date_gt_with", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return compareDateOrDateTime(fl, "date", "gt")
//	}
//}
//
//// 日期时间与另一字段日期时间对比大小，小于等于则返回true,如果两个字段值其一为空，则不比较，返回true
//func datetimeLteWith() (string, validate.FuncCtx) {
//	return "datetime_lte_with", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return compareDateOrDateTime(fl, "datetime", "lte")
//	}
//}
//
//// 日期时间与另一字段日期时间对比大小，小于则返回true,如果两个字段值其一为空，则不比较，返回true
//func datetimeLtWith() (string, validate.FuncCtx) {
//	return "datetime_lt_with", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return compareDateOrDateTime(fl, "datetime", "lt")
//	}
//}
//
//// 日期时间与另一字段日期时间对比大小，大于等于则返回true,如果两个字段值其一为空，则不比较，返回true
//func datetimeGteWith() (string, validate.FuncCtx) {
//	return "datetime_gte_with", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return compareDateOrDateTime(fl, "datetime", "gte")
//	}
//}
//
//// 日期时间与另一字段日期时间对比大小，大于则返回true,如果两个字段值其一为空，则不比较，返回true
//func datetimeGtWith() (string, validate.FuncCtx) {
//	return "datetime_gt_with", func(ctx context.Context, fl validate.FieldLevel) bool {
//		return compareDateOrDateTime(fl, "datetime", "gt")
//	}
//}
//
//// 日期或日期时间大小对比
//// dateType 区分日期类型，date日期，datetime日期时间
//// operator 比较类型,lte 小于等于，lt 小于，gte 大于等于, gt 大于
//func compareDateOrDateTime(fl validate.FieldLevel, dateType, operator string) bool {
//	fieldValue := fl.Field().String()
//	kind := fl.Field().Kind()
//	currentField, currentKind, ok := fl.GetStructFieldOK()
//	if !ok || currentKind != kind {
//		return false
//	}
//	currentValue := currentField.String()
//	// 没有数据，不做比较
//	if currentValue == "" || fieldValue == "" {
//		return true
//	}
//
//	defer func() {
//		if e := recover(); e != nil {
//			panic(fmt.Sprintf("compareDateOrDateTime: date convert error=%v,fieldValue=%s, targetValue=%s", e, fieldValue, currentValue))
//		}
//	}()
//	// 处理日期和日期时间关系
//	regexTarget := dateRegex
//	format := helpers.DateFormat
//	if dateType == "datetime" {
//		regexTarget = datetimeRegex
//		format = helpers.DateTimeFormat
//	}
//
//	// 不是正确时间格式
//	if !regexTarget.MatchString(fieldValue) || !regexTarget.MatchString(currentValue) {
//		return false
//	}
//
//	isValid := false
//	switch operator {
//	case "lte":
//		isValid = carbon.ParseByFormat(fieldValue, format).Lte(carbon.ParseByFormat(currentValue, format)) // 小于等于
//	case "lt":
//		isValid = carbon.ParseByFormat(fieldValue, format).Lt(carbon.ParseByFormat(currentValue, format)) // 小于
//	case "gte":
//		isValid = carbon.ParseByFormat(fieldValue, format).Gte(carbon.ParseByFormat(currentValue, format)) // 大于等于
//	case "gt":
//		isValid = carbon.ParseByFormat(fieldValue, format).Gt(carbon.ParseByFormat(currentValue, format)) // 大于
//	}
//	return isValid
//}
//
//func init() {
//	// 注册自定义验证规则
//	for _, validateFunc := range []func() (string, validate.FuncCtx){customEmail, normalDatetime, normalDate, normalTime, normalTime_H_S, startNumOne, trimString, leftTrimString, rightTrimString, dateLteWith, dateLtWith, dateGteWith, dateGtWith, datetimeLteWith, datetimeLtWith, datetimeGteWith, datetimeGtWith} {
//		tag, fn := validateFunc()
//		if err := validatorV2.RegisterValidationCtx(tag, fn); err != nil {
//			panic("RegisterValidationCtx tag:" + tag + " err:" + err.Error())
//		}
//	}
//
//	for alias, validateStr := range aliasMap {
//		validatorV2.RegisterAlias(alias, validateStr)
//	}
//}
